
#include "Shell.h"
#include <fstream>
#include <sys/stat.h>

using namespace std;

Shell::Shell()
{
	_SuperBlock = new SuperBlock();
	_LowLevelDisk = new LowLevelDisk();
	_systemCall = new SystemCall(_LowLevelDisk);

	mainRun();
}

Shell::~Shell() {
}

void Shell::mainRun()
{

	//cout<<sizeof(Inode)<<endl;
	//cout<<sizeof(int)<<endl;

//	_FILE_SYS = fopen("FILE_SYS", "rb+");

	//if FILE_SYS EXSITSE
//	if(_FILE_SYS != NULL)
//	{
//		cout<<"FILE_SYS already exists."<<endl;
//	}
//	else
//	{
		cout<<"FILE_SYS does not exists. creating a new FILE_SYS"<<endl;
		_FILE_SYS = fopen("FILE_SYS", "wb+");
		cout<<"FILE WAS CREATED"<<endl;

		cout << "reading config.txt"<<endl;
		ReadConfigFile("config.txt");
//	}

		_LowLevelDisk->setFileSys(_FILE_SYS, _SuperBlock);
		UpdateFreeInodeAddrs();
		UpdateFreeBlockAddrs();

	/*---------------------------------------/
	 * STUFF THAT WORKS:
	 * _LowLevelDisk->setFileSys(_FILE_SYS, _SuperBlock);
	 * _LowLevelDisk->setFreeInodesAddressesBlock();
	 * _LowLevelDisk->allocateInode(); //not yet
	 * UpdateFreeInodeAddrs();
	 * UpdateFreeBlockAddrs();
	 * _LowLevelDisk->setInodeType(in,0);
	 * _LowLevelDisk->getInodeType(in);
	 * _LowLevelDisk->allocateDataBlock();
	 * _LowLevelDisk->freeDataBlock(int db);
	 * _LowLevelDisk->setFileSize(in, newSize);
	 * _LowLevelDisk->getFileSize(in);
	 *
	 */

	// TRYing area:

		_systemCall->MakeFile("/ab/cd/ef/out.txt",0,1);


		/*int in = _LowLevelDisk->allocateInode();
		_LowLevelDisk->freeInode(in);
		for(int k = 0; k< _SuperBlock->getINodeArraySize() -1; k++)
		{
			if(k>40)
			{
				cout<<"for index is: "<<k<<endl;
			}
			in =_LowLevelDisk->allocateInode();
			cout<<_SuperBlock->getFreeInodesNum()<<" inodes left - in loop"<<endl;
		}
		cout<<_SuperBlock->getFreeInodesNum()<<" inodes left (after loop)"<<endl;
		_LowLevelDisk->freeInode(10);
		_LowLevelDisk->freeInode(15);
		_LowLevelDisk->freeInode(20);*/




		/*for(int k = 0; k< _SuperBlock->getINodeArraySize() -1; k++)
		{
			cout<<"freeing node number "<<k<<endl;
			_LowLevelDisk->freeInode(k);
			cout<<_SuperBlock->getFreeInodesNum()<<" inodes left - in loop"<<endl;
		}
*/

		//in = _LowLevelDisk->allocateInode();
		/*_LowLevelDisk->setInodeType(in,0);
		int type = _LowLevelDisk->getInodeType(in);
		cout<<"inode type is: "<<type<<endl;
		int db = _LowLevelDisk->allocateDataBlock();
		_LowLevelDisk->setDataBlock(in,0,db);
		_LowLevelDisk->freeDataBlock(db);

		Inode n;
		cout<<"inode file size: "<<(int)n.fileSize<<endl;
		cout<<"inode file type: "<<(int)n.type<<endl;
		cout<<"inode hard link number: "<<(int)n.hardLinksNumber<<endl;  */
	// End of TRYing area:


	/*---------------------------------------/
	 * FUNCTIONS TO CHECK:
	 * void freeInode(int inode); -- not finished yet.
	 *
	 * void int getDataBlock (int inode, int i);
	 * void setDataBlock (int inode, int i, int dblockNum );
	 * void readBlock(int dblockNum, char* buf) ;
	 * void writeBlock(int dblockNum, char* newdata);
	 *
	 */



	bool alive = true;
	string arg = "";
	string choice;
	while (alive)
	{
		cout << ">";
		cin >> choice;

		if(choice.compare("exit") == 0)
		{
			alive = false;
		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
		if(choice.compare(" ") == 0)
		{

		}
	}
	fclose(_FILE_SYS);
}

void Shell::ReadConfigFile(string arg)
{
	ifstream configFile;
	configFile.open(arg.c_str());
	string line;
	int temp;
	if (configFile.is_open())
	{
		string line;
		// get first argument
		getline(configFile, line);
		sscanf(line.c_str(), "dataBlockSize = %d", &temp);

		_SuperBlock->setBlockSize(temp);
		// get 2nd argument
		getline(configFile, line);
        sscanf (line.c_str(),"numberOfInodes = %d",&temp);
        _SuperBlock->setNodeArraySize(temp);
        // get 3rd argument
        getline(configFile, line);
        sscanf (line.c_str(),"diskSize = %d",&temp);
        _SuperBlock->setFileSystemSize(temp);
        //(passed to SB class) int numOfBlocks = ceil((double)_SuperBlock.getFileSystemSize()/_SuperBlock.getBlockSize());


/*		****moved to SB class****
        int numOfFreeBlocks = numOfBlocks - (3 + _SuperBlock.getFreeInodesNum()/(_SuperBlock.getBlockSize()/50));
        _SuperBlock.setFreeBlocksNum(numOfFreeBlocks);

        _SuperBlock.setFreeInodesNum(_SuperBlock.getNodeArraySize() - 1);
*/

        //_SuperBlock.printSB();
        configFile.close();
        _SuperBlock->calc();
        WriteSuperBlockToDisk();
        WriteInodesToDisk();
        writeRootInode();
	}
}

void Shell::WriteSuperBlockToDisk()
{
	int num;
	num = _SuperBlock->getFileSystemSize();	//file size
//	cout<<"file sys size: " <<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getBlockSize();	//block size
//	cout<<"block size: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getRootInodeNumber();	//root iNode number
//	cout<<"root inode number: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getFreeBlocksNum();	//number of free blocks
//	cout<<"free blocks number: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getFirstFreeBlockOffset();// first free block pointer location
//	cout<<"first free block offSet: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getLastBlockOffset();	// offset to last block (of free blocks)
//	cout<<"last block offset: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getINodeArraySize();	//node array size
//	cout<<"inode array size: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getFreeInodesNum();	//number of free iNodes
//	cout<<"free inode number: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getFirstFreeInodeOffset(); //first free inode (in super block)
//	cout<<"first free inode offset: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);

	num = _SuperBlock->getLastInodeOffset(); //last free inode pointer
//	cout<<"last inode offset: "<<num<<endl;
	fwrite(&num, sizeof(int), 1,_FILE_SYS);
}

void Shell::WriteInodesToDisk()
{

	int first = 3 * _SuperBlock->getBlockSize();
	int arraySize = _SuperBlock->getINodeArraySize();

	fseek(_FILE_SYS,first,SEEK_SET);
	Inode inode =_LowLevelDisk->newInode();
//	cout<<"Inode created: "<<endl;
//	_LowLevelDisk->printInode(inode);

//	fwrite(&inode,sizeof(Inode),arraySize,_FILE_SYS); // write all inodes in a row
	for (int i = 0; i < _SuperBlock->getINodeArraySize(); ++i) {
		fwrite(&inode,sizeof(Inode),1,_FILE_SYS);
//		cout<<"Inode: "<<i<<endl;
//		_LowLevelDisk->printInode(inode);
	}
}

//puts addresses of free links in appropriate places
void Shell::UpdateFreeInodeAddrs()
{
	int firstAddOff = _SuperBlock->getFirstFreeInodeOffset(); //offset of first free inode address
	int firstFreeInodeOff = _SuperBlock->getInodeArrayOffset() + sizeof(Inode); // offset of first free inode
	int blocksize = _SuperBlock->getBlockSize();
	int ptsPerBlck = blocksize/4 -1; //excluding last pointer-to next block


	//	writing addresses in superBLock (except last)
	int iNodeOff = firstFreeInodeOff;
	fseek(_FILE_SYS,firstAddOff,SEEK_SET);
	for (int i = 0; i < ptsPerBlck; ++i,iNodeOff+=sizeof(Inode))
	{
		fwrite(&iNodeOff,sizeof(int),1,_FILE_SYS);
	}
	//last entry in block is pointer to next block
	int a = _SuperBlock->getFreeInodeListOffsetOutsideSB(); //address of 2nd node in list
	fwrite(&a,sizeof(int),1,_FILE_SYS);



	//*****  writing addresses to rest of adds blocks ******************

						//= total  - root - in superblock
	int freeInodesLeft = _SuperBlock->getINodeArraySize() - 1 - ptsPerBlck;
	int listNOdes = _SuperBlock->getNumOfInodeListNodes() -1 ;
	//writing addresses to rest of adds blocks
	fseek(_FILE_SYS,a,SEEK_SET); //moving fd to beginning of free addresses

	for (int i = 0; i < listNOdes-1; ++i)   // for each address block , -1 becase 1 extra
	{
		for (int j = 0; (j < ptsPerBlck) & (freeInodesLeft>0); ++j,iNodeOff+=sizeof(Inode))
		{
			fwrite(&iNodeOff,sizeof(int),1,_FILE_SYS);
			freeInodesLeft--;
		}
		if (freeInodesLeft > 0){
			long int ptr =ftell(_FILE_SYS)+4;
			fwrite(&ptr,sizeof(int),1,_FILE_SYS); //pointer to next block
		}
	}

	//adding pointer to extra block
	int tmp = ftell (_FILE_SYS);
	int newAddr = _SuperBlock->getBeginningOfBlock(tmp) + blocksize; //next block
	int lastPtr = newAddr -4;	//last pointer addr
	fseek(_FILE_SYS, lastPtr,SEEK_SET);
	fwrite(&newAddr,sizeof(int),1,_FILE_SYS);
	int i = 0;
	fseek(_FILE_SYS,lastPtr+blocksize,SEEK_SET);
	fwrite(&i,sizeof(int),1,_FILE_SYS);
}


//puts addresses of free links in appropriate places
void Shell::UpdateFreeBlockAddrs()
{
	int firstAddOff = _SuperBlock->getFirstFreeBlockOffset(); //offset of address of first free block
	int lastInode = _SuperBlock->getLastInodeOffset();
	int blocksize = _SuperBlock->getBlockSize();
	int firstFreeBlockOff =lastInode - (lastInode % blocksize) + 1*blocksize; //TODO: consider  root - inode first block = root inode block
	int ptsPerBlck = blocksize/4 -1; //excluding last pointer-to next block

	//	writing addresses in superBLock (except last)
	int blockOff = firstFreeBlockOff;
	fseek(_FILE_SYS,firstAddOff,SEEK_SET);

	for (int i = 0; i < ptsPerBlck; ++i,blockOff+=blocksize) {
		fwrite(&blockOff,sizeof(int),1,_FILE_SYS);
	}
	//last entry in block is pointer to next block
	int secondBlockOff = _SuperBlock->getFreeBlockListOffsetOutsideSB(); //address of 2nd node in list
	fwrite(&secondBlockOff,sizeof(int),1,_FILE_SYS);


	//*****  writing addresses to rest of adds blocks ******************
	//blockOff is increased in every iteration

	int freeBlocksLeft = _SuperBlock->getFreeBlocksNum() - ptsPerBlck;	//= total   - in superblock
	int listNodes = _SuperBlock->getNumOfBlockListNodes() -1 ; //number of nodes in list (of free blocks)
	//writing addresses to rest of adds blocks
	fseek(_FILE_SYS,secondBlockOff,SEEK_SET); //moving fd to beginning of free addresses
	long int tmp;
	for (int i = 0; i < listNodes-1; ++i)   // for each address block
	{
		for (int j = 0; (j < ptsPerBlck) && (freeBlocksLeft>0) ; ++j,blockOff+=blocksize)
		{
			fwrite(&blockOff,sizeof(int),1,_FILE_SYS);
			freeBlocksLeft--;
		}
		if (freeBlocksLeft > 0){
			long int ptr =ftell(_FILE_SYS)+4;
			fwrite(&ptr,sizeof(int),1,_FILE_SYS); //pointer to next block
		}
	}


	//adding pointer to extra block
	tmp = ftell (_FILE_SYS);
	int newAddr = _SuperBlock->getBeginningOfBlock(tmp) + blocksize; //next block
	int lastPtr = newAddr -4;	//last pointer addr
	fseek(_FILE_SYS, lastPtr,SEEK_SET);
	fwrite(&newAddr,sizeof(int),1,_FILE_SYS);
	int i = 0;
	fseek(_FILE_SYS,lastPtr+blocksize,SEEK_SET);
	fwrite(&i,sizeof(int),1,_FILE_SYS);


}

void Shell::writeRootInode()
{
	int offset = _SuperBlock->getInodeArrayOffset();
	Inode inode = _LowLevelDisk->newInode();
	inode.type = 1;
	fseek(_FILE_SYS,offset,SEEK_SET);
	fwrite(&inode,sizeof(Inode),1,_FILE_SYS);
}




